#include "SystemCallLayer.h"

SystemCallLayer::SystemCallLayer():	fileSystem(),lowestFreeDescriptorNum(0), currProcessID(-1) {
	openFilesDescTable.clear();
	currProcessFileDescTable = new map<int,FileDescEntry*>;
	inodeLocksTable.clear();
}

SystemCallLayer::SystemCallLayer(int diskSizeInBytes, int dataBlockSize,
		int numberOfInodes): fileSystem(diskSizeInBytes, dataBlockSize, numberOfInodes),
				lowestFreeDescriptorNum(0), currProcessID(-1) {
			char root[] = ".";
			MakeDir(root);
			openFilesDescTable.clear();
			currProcessFileDescTable = new map<int,FileDescEntry*>;
			inodeLocksTable.clear();
}

SystemCallLayer::~SystemCallLayer(){}

//returns fd if the type is "regular" or fileInode is type is "soft-link"
int SystemCallLayer::MakeFile(const char* filename, string type, string flag) {
	int fileInode = fileSystem.createFile (type);

	if (fileInode == -1) {
		cerr << "Cannot create file" << endl;
		return -1;
	}
	fileSystem.incInodeHardLinks(fileInode);
	string stringPath = filename;
	vector<string> pathDirNames = parsePath(stringPath);
	const char *onlyFileName = (pathDirNames.back()).c_str();
	vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,false);
	if (pathInodes.size() == 0) {
		cerr << "Cannot create file - " << filename << " is an incorrect path" << endl;
		return -1;
	}
	vector<DirectoryEntry*> currDirEntries = fileSystem.d_read (pathInodes.back());
	for (int i=0; i<(int)currDirEntries.size(); i++) {
		if (strcmp(currDirEntries[i]->getFileName(),onlyFileName) == 0) {
			cerr << "Cannot create file " << filename << ""
				 " because a file with the same name already exists" << endl;
			return -1;
		}
	}
	DirectoryEntry *newFileEntry = new DirectoryEntry(fileInode, onlyFileName);
	currDirEntries.push_back(newFileEntry);
	fileSystem.d_write (pathInodes.back(), currDirEntries);
	if (type.compare("soft-link") == 0) {
		return OpenForSoftLinkFill (filename, flag);
	} else {
		return Open(filename, flag);
	}
}

int SystemCallLayer::MakeHLink (char* targetFileName, char* fileName) {
	vector<string> newPathDirNames = parsePath(fileName);
	vector<string> targetPathDirNames = parsePath(targetFileName);

	const char *onlyNewFileName = (newPathDirNames.back()).c_str();

	vector<int> newPathInodes = findPathDirsInodeNums(newPathDirNames,false);
	vector<int> oldPathInodes = findPathDirsInodeNums(targetPathDirNames,true);

	if (newPathInodes.size() == 0) {
		cerr << "Cannot create HLink - " << fileName << " is an incorrect path" << endl;
		return -1;
	}
	if (oldPathInodes.size() == 0) {
		cerr << "Cannot create HLink - " << targetFileName << " is an incorrect path" << endl;
		return -1;
	}
	vector<DirectoryEntry*> currDirEntries = fileSystem.d_read (newPathInodes.back());
	for (int i=0; i<(int)currDirEntries.size(); i++) {
		if (strcmp(currDirEntries[i]->getFileName(),onlyNewFileName) == 0) {
			cerr << "Cannot create file " << fileName << ""
				 " because a file with the same name already exists" << endl;
			return -1;
		}
	}
	DirectoryEntry *newFileEntry = new DirectoryEntry(oldPathInodes.back(), onlyNewFileName);
	fileSystem.incInodeHardLinks(oldPathInodes.back());
	currDirEntries.push_back(newFileEntry);
	fileSystem.d_write (newPathInodes.back(), currDirEntries);
	//success
	return 0;
}

int SystemCallLayer::MakeDir (const char* dirName) {
	int dirInodeNum = fileSystem.createDir();
	if (dirInodeNum == -1) {
		cerr << "cannot create dir" << endl;
	} else {
		string stringPathName = dirName;
		vector<string> pathDirNames = parsePath(stringPathName);
		vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,false);
		//the path is deeper than just "/"
		char dot[] = ".";
		char doubleDot[] = "..";
		if (strcmp(dirName,".") != 0) {
			int containingDirInode = pathInodes.at(pathInodes.size()-1);
			//adds the new dir to its parrent directory + adds the "." and ".." directories
			string newDirName = pathDirNames.at(pathDirNames.size()-1);
			if (newDirName.size()>12) {
				cerr << "directory name is too long!!!" << endl;
				return -1;
			}
			char charNewDirName[13];
			//converts the name of the new directory from string to char*
			for (int i=0; i<(int)newDirName.size(); i++) {
				charNewDirName[i] = newDirName.at(i);
			}
			for (int i = (int)newDirName.size(); i<13; i++) {
				charNewDirName[i] = 0;
			}
			vector<DirectoryEntry*> newDirVec = fileSystem.d_read(containingDirInode);
			for (int i=0; i<(int)newDirVec.size(); i++) {
				if (strcmp(newDirVec[i]->getFileName(),charNewDirName) == 0) {
					cerr << "Cannot create directory " << charNewDirName << ""
						 " because a directory with the same name already exists" << endl;
					return -1;
				}
			}
			addDirEntry(containingDirInode, dirInodeNum, charNewDirName);
			addDirEntry(dirInodeNum, dirInodeNum, dot);
			addDirEntry(dirInodeNum, containingDirInode, doubleDot);

		} else {
			addDirEntry(0,0,dot);
			addDirEntry(0,0,doubleDot);
		}

		fileSystem.incInodeHardLinks(dirInodeNum);
	}
	return dirInodeNum;
}

int SystemCallLayer::RmDir (const char* dirName) {
	//finds the real dir name (needed in case this is a soft-link):
	string stringPathName;
	findDir(dirName, stringPathName);
	if (stringPathName.compare("") == 0) {
		cout << "directory " << dirName << " doesn't exists or this is a broken soft-link" << endl;
		return -1;
	}
	vector<string> pathDirNames = parsePath(stringPathName);
	//extract the name of the desired directory for deletion
	string stringDirName = pathDirNames.at(pathDirNames.size()-1);
	vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,true);
	if (stringPathName.compare("/") == 0) {
		cerr << "cannot delete \"/\"" << endl;
		return -1;
	}
	if (pathInodes.size() == 0) {
		cout << "directory " << stringPathName << " doesn't exists" << endl;
		return -1;
	}

	int parentDirInode = pathInodes.at(pathInodes.size()-2);
	vector<DirectoryEntry*> currentDirContent = fileSystem.d_read(parentDirInode);
	vector<DirectoryEntry*>::iterator it = currentDirContent.begin();
	int dirInodeNum = -1;
	bool found = false;
	while (!found && it != currentDirContent.end()) {
		DirectoryEntry* currDE = *it;
		if (strcmp((currDE->getFileName()),stringDirName.c_str()) == 0) {
			found = true;
			dirInodeNum = currDE->getINodeNum();
			//if contains only the default directories "." and ".." - directory is empty
			if (fileSystem.getFileSize(dirInodeNum) == 32) {
				currentDirContent.erase(it);
			} else {
				cerr << "directory is not empty!! " << endl;
				return -1;
			}
		}
		it++;
	}
	if (dirInodeNum != -1) {
		fileSystem.decInodeHardLinks(dirInodeNum);
		fileSystem.d_delete(dirInodeNum);
		fileSystem.d_write(parentDirInode,currentDirContent);

	} else {
		cout << "directory " << stringPathName << " doesn't exists" << endl;
		return -1;
	}
	return 0;
}

int SystemCallLayer::RmFile (const char* fileName) {
	string stringPathName = fileName;
	vector<string> pathDirNames = parsePath(stringPathName);
	//extract the name of the desired file for deletion
	string stringFileName = pathDirNames.at(pathDirNames.size()-1);
	vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,true);
	if (strcmp(fileName,"/") == 0) {
		cerr << "cannot delete \"/\"" << endl;
		return -1;
	}
	if (pathInodes.size() == 0) {
		cout << "file " << fileName << " doesn't exists" << endl;
		return -1;
	}
	int parentDirInode = pathInodes.at(pathInodes.size()-2);
	vector<DirectoryEntry*> currentDirContent = fileSystem.d_read(parentDirInode);
	vector<DirectoryEntry*>::iterator it = currentDirContent.begin();
	int fileInodeNum = -1;
	bool found = false;
	while (!found && it != currentDirContent.end()) {
		DirectoryEntry* currDE = *it;
		if (strcmp((currDE->getFileName()),stringFileName.c_str()) == 0) {
			found = true;
			fileInodeNum = currDE->getINodeNum();
			currentDirContent.erase(it);
		}
		it++;
	}
	if (fileInodeNum != -1) {
		fileSystem.decInodeHardLinks(fileInodeNum);
		fileSystem.f_delete(fileInodeNum);
		fileSystem.d_write(parentDirInode,currentDirContent);

	} else {
		cout << "file " << fileName << " doesn't exists" << endl;
	}
	return fileInodeNum;
}

int SystemCallLayer::getFileSize(int inode) {
	return fileSystem.getFileSize(inode);
}

int SystemCallLayer::getNumOfFileInDir(char* dirName) {
	string stringPathName = dirName;
	vector<string> pathDirNames = parsePath(stringPathName);
	//extract the name of the desired directory for deletion
	string stringDirName;
	int desiredDirInode;
	if (pathDirNames.size() == 0) {
		stringDirName = "/";
		desiredDirInode = 0;
	} else {
		stringDirName = pathDirNames.at(pathDirNames.size()-1);
		vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,true);
		desiredDirInode = pathInodes.at(pathInodes.size()-1);
	}

	if (fileSystem.getFileType(desiredDirInode) != 'd') {
		cerr << dirName << " is not a directory!!!" << endl;
		return -1;
	}
	vector<DirectoryEntry*> currentDirContent = fileSystem.d_read(desiredDirInode);

	return currentDirContent.size();
}


int SystemCallLayer::ls(char* dirName, char* buf) {
	string stringPathName = dirName;
	vector<string> pathDirNames = parsePath(stringPathName);
	//extract the name of the desired directory for deletion
	int desiredDirInode;
	if (pathDirNames.size() == 0) {
		desiredDirInode=0;
	} else {
		vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,true);
		desiredDirInode = pathInodes.at(pathInodes.size()-1);
	}
	if (fileSystem.getFileType(desiredDirInode) != 'd') {
		cerr << dirName << " is not a directory!!!" << endl;
		return -1;
	}
	vector<DirectoryEntry*> currentDirContent = fileSystem.d_read(desiredDirInode);
	vector<DirectoryEntry*>::iterator dirIt;
	ostringstream fileList;
	for (dirIt = currentDirContent.begin(); dirIt != currentDirContent.end(); dirIt++) {
		char* fileName = (*dirIt)->getFileName();
		int fileInode = (*dirIt)->getINodeNum();
		int fileSize = fileSystem.getFileSize(fileInode);
		fileList << fileSize << " ";
		if (fileSystem.getFileType(fileInode) == 'd') {
			fileList << "/" << fileName << endl;
		} else {
			fileList << fileName << endl;
		}
	}
	//closes the stream
	fileList << '\0';
	string fileListString = fileList.str();
	for (int i=0; i<(int)fileListString.size(); i++) {
		buf[i] = fileListString[i];
	}

	return 0;
}

int SystemCallLayer::OpenForSoftLinkFill (const char* fileName, string flag) {
	int fd = -1;

	if (lowestFreeDescriptorNum == -1) {
		cerr << "Cannot open file " << fileName << "- No free entries in the process FD table." << endl;
		return -1;
	}

	string stringPath = fileName;
	vector<string> pathDirNames = parsePath(stringPath);
	const char *onlyFileName = (pathDirNames.back()).c_str();
	vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,false);
	if (pathInodes.size() == 0) {
		cerr << "Cannot open file " << onlyFileName << "- Incorrect path." << endl;
		return -1;
	}
	vector<DirectoryEntry*> currDirEntries = fileSystem.d_read (pathInodes.back());
	int fileInodeNum = -1;
	for (int i=0; i<(int)currDirEntries.size(); i++) {
		char *currEntryName = currDirEntries[i]->getFileName();
		if (strcmp(currEntryName,onlyFileName) == 0) {
			fileInodeNum = currDirEntries[i]->getINodeNum();
			break;
		}
	}

	if (fileInodeNum == -1) {
		cerr << "Cannot open file " << onlyFileName << "- file not found in path." << endl;
		return -1;
	}

	FileDescEntry *newFileEntry = new FileDescEntry (0, flag, fileInodeNum);
	openFilesDescTable.push_back(newFileEntry);
	currProcessFileDescTable->insert(pair<int,FileDescEntry*> (lowestFreeDescriptorNum, newFileEntry));
	fd = lowestFreeDescriptorNum;
	findNextLowestFreeDescriptorNum();
	return fd;
}

int SystemCallLayer::Open (const char* fileName, string flag) {
	int fd = -1;

	if (lowestFreeDescriptorNum == -1) {
		cerr << "Cannot open file " << fileName << "- No free entries in the process FD table." << endl;
		return -1;
	}

	string stringPath = fileName;
	vector<string> pathDirNames = parsePath(stringPath);
	const char *onlyFileName = (pathDirNames.back()).c_str();
	vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,false);
	if (pathInodes.size() == 0) {
		cerr << "Cannot open file " << onlyFileName << "- Incorrect path." << endl;
		return -1;
	}
	vector<DirectoryEntry*> currDirEntries = fileSystem.d_read (pathInodes.back());
	int fileInodeNum = -1;
	for (int i=0; i<(int)currDirEntries.size(); i++) {
		char *currEntryName = currDirEntries[i]->getFileName();
		if (strcmp(currEntryName,onlyFileName) == 0) {
			fileInodeNum = currDirEntries[i]->getINodeNum();
			break;
		}
	}

	if (fileInodeNum == -1) {
		cerr << "Cannot open file " << onlyFileName << "- file not found in path." << endl;
		return -1;
	}
	//if it is a directory
	if (fileSystem.getFileType(fileInodeNum) == 'd') {
		cerr << onlyFileName << " is a directory. It can`t be opened with the command \"open\" "<< endl;
		return -1;
	}
	//if it is a soft link:
	if (fileSystem.getFileType(fileInodeNum) == 's') {
		int softLinkSize = fileSystem.getFileSize(fileInodeNum);
		unsigned char buffer[softLinkSize];
		fileSystem.f_read(fileInodeNum,buffer,0,softLinkSize);
		char softLinkAddress[softLinkSize];
		//converts unsiged char[] into char[]
		for (int i=0; i<softLinkSize; i++) {
			softLinkAddress[i] = buffer[i];
		}
		return Open((char*)buffer, flag);
	}

	//if it is a file:
	if (fileSystem.getFileType(fileInodeNum) == 'f') {
		FileDescEntry *newFileEntry = new FileDescEntry (0, flag, fileInodeNum);
		openFilesDescTable.push_back(newFileEntry);
		currProcessFileDescTable->insert(pair<int,FileDescEntry*> (lowestFreeDescriptorNum, newFileEntry));
		fd = lowestFreeDescriptorNum;
		findNextLowestFreeDescriptorNum();
	}
	cout << "Process " << currProcessID << " opened file " << fileName << " with permissions "
			"" << flag << " and it`s fd is " << fd << endl;
	return fd;
}

int SystemCallLayer::Close (int fd) {
	map<int,FileDescEntry*>::iterator currProcessFDTableIter = currProcessFileDescTable->find(fd);
	if (currProcessFDTableIter == currProcessFileDescTable->end()) {
		cerr << "Cannot close file descriptor " << fd << " - it wasn't opened" << endl;
		return -1;
	} else {

		vector<FileDescEntry*>::iterator openFDTableIter = openFilesDescTable.begin();
		while (openFDTableIter != openFilesDescTable.end()) {
			//if the entry is found in the openFDtable vector
			if ((*openFDTableIter) == currProcessFDTableIter->second) {
				(*openFDTableIter)->decNumOfConnectedProcesses();
				if ((*openFDTableIter)->getNumOfConnectedProcesses() == 0) {
					openFilesDescTable.erase(openFDTableIter);
				}
				break;
			}
			openFDTableIter++;
		}

		//releases the lock of the file connected to fd, if exists
		int fileInode = currProcessFDTableIter->second->getPointerToInode();
		map<int,Lock>::iterator lockIter = inodeLocksTable.find(fileInode);
		if (lockIter != inodeLocksTable.end()) {
			if (currProcessID == lockIter->second.getProcessID()) {
				if (lockIter->second.getKind().compare("read-only") == 0) {
					releaseLockRead(fd);
				} else {
					releaseLockWrite(fd);
				}
			}
		}

		//erases the entry from the currProcessFileDescTable
		currProcessFileDescTable->erase(currProcessFDTableIter);
		if (fd < lowestFreeDescriptorNum) {
			lowestFreeDescriptorNum = fd;
		}
	}

	return 0;
}

int SystemCallLayer::Seek (int fd, int location) {
	map<int,FileDescEntry*>::iterator currProcessFDTableIter = currProcessFileDescTable->find(fd);
	if (currProcessFDTableIter == currProcessFileDescTable->end()) {
		cerr << "Cannot seek file descriptor " << fd << "- it doesn't exist" << endl;
		return -1;
	} else {
		currProcessFDTableIter->second->setFilePosition(location);
	}
	return 0;
}

int SystemCallLayer::Read (int fd, int nBytes, unsigned char* buffer) {
	map<int,FileDescEntry*>::iterator currProcessFDTableIter = currProcessFileDescTable->find(fd);
	if (currProcessFDTableIter == currProcessFileDescTable->end()) {
		cerr << "Cannot read using file descriptor " << fd << "- it doesn't exist" << endl;
		return -1;
	} else {
		int inode = currProcessFDTableIter->second->getPointerToInode();
		if (lockCheck(inode, "read-only")
				&& permissionsCheck(currProcessFDTableIter->second->getFilePermission(), "read-only")) {
			int amountRead = fileSystem.f_read(inode, buffer,
					currProcessFDTableIter->second->getFilePosition(), nBytes);

			map<int,FileDescEntry*>::iterator currProcessFDTableIter = currProcessFileDescTable->find(fd);
			int currPosition = currProcessFDTableIter->second->getFilePosition();
			Seek (fd, currPosition + amountRead);
			return amountRead;
		} else {
			return -1;
		}
	}
}

int SystemCallLayer::Write (int fd, int nBytes, unsigned  char* buffer) {
	map<int,FileDescEntry*>::iterator currProcessFDTableIter = currProcessFileDescTable->find(fd);
	if (currProcessFDTableIter == currProcessFileDescTable->end()) {
		cerr << "Cannot write using file descriptor " << fd << "- it doesn't exist" << endl;
		return -1;
	} else {
		int inode = currProcessFDTableIter->second->getPointerToInode();
		if (lockCheck(inode, "read-and-write")
				&& permissionsCheck(currProcessFDTableIter->second->getFilePermission(), "read-and-write")) {
			int currPosition = currProcessFDTableIter->second->getFilePosition();
			int amountWrote = fileSystem.f_write(inode, buffer, currPosition, nBytes);
			Seek (fd, currPosition + amountWrote);
			cout << "amountWrote: " << amountWrote << endl;
			return amountWrote;
		} else {
			return -1;
		}
	}
}

int SystemCallLayer::lockRead (int fd) {
	map<int,FileDescEntry*>::iterator currProcessFDTableIter = currProcessFileDescTable->find(fd);
	if (currProcessFDTableIter == currProcessFileDescTable->end()) {
		cerr << "Cannot lock using file descriptor " << fd << "- it doesn't exist" << endl;
		return -1;
	} else {
		int fileInode = currProcessFDTableIter->second->getPointerToInode();
		map<int,Lock>::iterator iter = inodeLocksTable.find(fileInode);
		if (iter == inodeLocksTable.end()) {
			//the inode isn't locked
			Lock newLock (currProcessID, "read-only");
			inodeLocksTable.insert(pair<int,Lock> (fileInode, newLock));
			return 0;
		} else {
			cerr << "Process " << currProcessID << " cannot lock inode " << fileInode <<
					" for read only because it is already locked by process " <<
					iter->second.getProcessID() << " for " << iter->second.getKind() << endl;
			return -1;
		}
	}
}

int SystemCallLayer::lockWrite (int fd) {
	map<int,FileDescEntry*>::iterator currProcessFDTableIter = currProcessFileDescTable->find(fd);
	if (currProcessFDTableIter == currProcessFileDescTable->end()) {
		cerr << "Cannot lock using file descriptor " << fd << "- it doesn't exist" << endl;
		return -1;
	} else {
		int fileInode = currProcessFDTableIter->second->getPointerToInode();
		map<int,Lock>::iterator iter = inodeLocksTable.find(fileInode);
		if (iter == inodeLocksTable.end()) {
			//the inode isn't locked
			Lock newLock (currProcessID, "read-and-write");
			inodeLocksTable.insert(pair<int,Lock> (fileInode, newLock));
			return 0;
		} else {
			cerr << "Process " << currProcessID << " cannot lock inode " << fileInode <<
					" for read and write because it is already locked by process " <<
					iter->second.getProcessID() << " for " << iter->second.getKind() << endl;
			return -1;
		}
	}
}

int SystemCallLayer::releaseLockRead (int fd) {
	map<int,FileDescEntry*>::iterator currProcessFDTableIter = currProcessFileDescTable->find(fd);
	if (currProcessFDTableIter == currProcessFileDescTable->end()) {
		cerr << "Cannot release lock using file descriptor " << fd << "- it doesn't exist" << endl;
		return -1;
	} else {
		int fileInode = currProcessFDTableIter->second->getPointerToInode();
		map<int,Lock>::iterator iter = inodeLocksTable.find(fileInode);
		//lock doesn't exist
		if (iter == inodeLocksTable.end()) {
			cerr << "Process " << currProcessID << " cannot release read lock of inode " <<
					fileInode << " because it is not locked" << endl;
			return -1;
		//ownership check
		} if (iter->second.getProcessID() != currProcessID) {
			cerr << "Process " << currProcessID << " cannot release read lock of inode " <<
					fileInode << " because it is not the owner of the lock" << endl;
			return -1;
		//if he is the owner of the lock we should check the lock`s type
		} if (iter->second.getKind().compare("read-and-write") == 0) {
			cerr << "Process " << currProcessID << " cannot release read lock of inode " <<
					fileInode << " because the lock is not read-only" << endl;
			return -1;
		} else {
			//all`s good - release lock
			inodeLocksTable.erase(iter);
			return 0;
		}
	}
}

int SystemCallLayer::releaseLockWrite (int fd) {
	map<int,FileDescEntry*>::iterator currProcessFDTableIter = currProcessFileDescTable->find(fd);
	if (currProcessFDTableIter == currProcessFileDescTable->end()) {
		cerr << "Cannot release lock using file descriptor " << fd << "- it doesn't exist" << endl;
		return -1;
	} else {
		int fileInode = currProcessFDTableIter->second->getPointerToInode();
		map<int,Lock>::iterator iter = inodeLocksTable.find(fileInode);
		//lock doesn't exist
		if (iter == inodeLocksTable.end()) {
			cerr << "Process " << currProcessID << " cannot release write lock of inode " <<
					fileInode << " because it is not locked" << endl;
			return -1;
		//ownership check
		} if (iter->second.getProcessID() != currProcessID) {
			cerr << "Process " << currProcessID << " cannot release write lock of inode " <<
					fileInode << " because it is not the owner of the lock" << endl;
			return -1;
		//if he is the owner of the lock we should check the lock`s type
		} if (iter->second.getKind().compare("read-only") == 0) {
			cerr << "Process " << currProcessID << " cannot release write lock of inode " <<
					fileInode << " because the lock is not read-and-write" << endl;
			return -1;
		} else {
			//all`s good - release lock
			inodeLocksTable.erase(iter);
			return 0;
		}
	}
}


int SystemCallLayer::moveFile(const char* oldName, const char* newName) {
	if (strcmp(oldName,"/") == 0) {
		cerr << "cannot move \"/\"" << endl;
		return -1;
	}
	//removing the directory entry from the old location
	string stringOldAbsoluteName = oldName;
	vector<string> oldDirNames = parsePath(stringOldAbsoluteName);
	string stringOldName = oldDirNames.at(oldDirNames.size()-1);
	vector<int> oldPathInodes = findPathDirsInodeNums(oldDirNames,false);
	int oldDirInode = oldPathInodes.back();
	vector<DirectoryEntry*> currentOldDirContent = fileSystem.d_read(oldDirInode);
	vector<DirectoryEntry*>::iterator dirIt = currentOldDirContent.begin();
	bool found = false;
	int oldNameInode = -1;
	//finds the oldName in the directory containing it
	while (!found && dirIt != currentOldDirContent.end()) {
		char* fileName = (*dirIt)->getFileName();
		if (strcmp(fileName, stringOldName.c_str()) == 0) {
			found = true;
			oldNameInode = (*dirIt)->getINodeNum();
		}
		dirIt++;
	}
	//revert the last inc
	dirIt--;

	if (oldNameInode == -1) {
		cerr << oldName << " does not exist!!!" << endl;
		return -1;
	}

	//adding the directory entry to the new location
	string stringNewAbsoluteName = newName;
	vector<string> newDirNames = parsePath(stringNewAbsoluteName);
	const char *onlyNewFileName = (newDirNames.back()).c_str();
	string stringNewName = newDirNames.at(newDirNames.size()-1);
	vector<int> newPathInodes = findPathDirsInodeNums(newDirNames,false);
	int newDirInode = newPathInodes.back();
	vector<DirectoryEntry*> currentNewDirContent = fileSystem.d_read(newDirInode);
	for (int i=0; i<(int)currentNewDirContent.size(); i++) {
		if (strcmp(currentNewDirContent[i]->getFileName(),onlyNewFileName) == 0) {
			cerr << "Cannot move file " << oldName << " to " << newName << ""
				 " because a file with the same name already exists" << endl;
			return -1;
		}
	}
	//deletes the old file entry
	currentOldDirContent.erase(dirIt);
	fileSystem.d_write(oldDirInode,currentOldDirContent);

	//creates a new entry inside the new directory
	//reads the content again, in case the newDir = oldDir
	currentNewDirContent = fileSystem.d_read(newDirInode);
	DirectoryEntry newFileEntry(oldNameInode,stringNewName.c_str());
	currentNewDirContent.push_back(&newFileEntry);
	fileSystem.d_write(newDirInode,currentNewDirContent);
	return 0;
}


void SystemCallLayer::findDir (const char* address, string& realAddress) {
	string stringPathName = address;
	vector<string> pathDirNames = parsePath(stringPathName);
	//extract the name of the desired directory for deletion
	int desiredInode;
	if (pathDirNames.size() == 0) {
		desiredInode=0;
	} else {
		vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,true);
		//the directory was not found
		if (pathInodes.size() == 0) {
			realAddress = "";
			return;
		}
		desiredInode = pathInodes.at(pathInodes.size()-1);
	}

	if (fileSystem.getFileType(desiredInode) == 'd') {
		realAddress = address;
		return;

	} else if (fileSystem.getFileType(desiredInode) == 's') {
		int softLinkSize = fileSystem.getFileSize(desiredInode);
		unsigned char buffer[softLinkSize];
		fileSystem.f_read(desiredInode,buffer,0,softLinkSize);
		char softLinkAddress[softLinkSize];
		//converts unsiged char[] into char[]
		for (int i=0; i<softLinkSize; i++) {
			softLinkAddress[i] = buffer[i];
		}
		softLinkAddress[softLinkSize] = 0;
		findDir(softLinkAddress, realAddress);
		return;

	} else {
		realAddress = "";
		return;
	}
}

void SystemCallLayer::test() {
	char name1[] = "/daphna";
	char name2[] = "/daphna/barak";
	unsigned char name20[] =  "/daphna/barak";
	char name3[] = "/dummy_sl";
	char name30[] = "/dummy_sl_sl";
	unsigned char name33[] = "/dummy_sl";

	char name4[] = "/dummy_hd";
	char name5[] = "/softlink";
	char name6[] = "/daphna/barak/dummy";
	unsigned char name60[] = "/daphna/barak/dummy";
	int fd1,fd2;

	cout << "creating dir daphna, inode =  "<< MakeDir(name1) << endl;
	cout << "creating dir barak, inode =  "<< MakeDir(name2) << endl;
	//cout << "creating dir barak, inode =  "<< MakeDir(name2) << endl;
	fd1 = MakeFile(name6,"regular", "read-only");
	//fd1 = MakeFile(name6,"regular", "read-and-write");
	/*cout << "creating dir dummy, inode =  "<< MakeDir(name3) << endl;

	cout << "deleting dir daphna, inode =  "<< RmDir(name1) << endl;
	cout << "deleting dir barak, inode =  "<< RmDir(name2) << endl;
	cout << "deleting dir dummy, inode =  "<< RmDir(name3) << endl;*/



	int softLinksInode = MakeFile(name3,"soft-link","read-and-write");
	fillSoftLink(softLinksInode,name60);
	fd1 = Open(name3,"read-and-write");


	/*int softLinksoftLinkInode = MakeFile(name30,"soft-link","read-and-write");
	fillSoftLink(softLinksoftLinkInode,name33,10);
	if (isDir(name30)) {
		cout << "DIR!!" << endl;
	} else {
		cout << "NOT DIR!!" << endl;
	}
	fd1 = MakeFile(name6,"regular", "read-and-write");
	fd2 = Open(name6,"read-and-write");
	unsigned char data1[] = "123456789";
	unsigned char data2[] = "00000";
	Write(fd1,10,data1);
	Seek(fd2,5);
	Write(fd2,5,data2);
	Seek(fd2,0);
	unsigned char readFromFile[20];
	Read(fd2,9,readFromFile);
	cout << readFromFile << endl;

*/
	//cout << "creating and opening file /dummy, fd = " << fd << endl;
	//cout << "closing file /dummy, fd = " << Close(fd) << endl;
	//cout << "opening file /dummy, fd = " << Open(name3,"read-and-write") << endl;
	//cout << "creating file /daphna/barak/dummy, inode = " << MakeFile(name6,"regular","read-and-write") << endl;
	//cout << "creating file /daphna/barak/dummy, inode = " << Open(name6,"read-and-write") << endl;


	//cout << "making HD to file /dummy, inode = " << MakeHLink(name3,name4) << endl;
	//cout << "creating file /softlink (softlink to /dummy), inode = " << MakeFile(name5,"soft-link","0666") << endl;
	//cout << "creating file /dummy, inode = " << MakeFile(name3,"regular","0666") << endl;

	//cout << "deleting dir /daphna, inode =  "<< RmFile(name1) << endl;
	//cout << "deleting dir daphna, inode =  "<< RmDir(name2) << endl;
	//cout << "deleting dir /dummy, inode =  "<< RmFile(name3) << endl;
	/*char dir[] = "/";
	int sizeOfDir = getNumOfFileInDir(dir);
	char buf[sizeOfDir*20];
	ls (dir,buf);
	cout << buf << endl;

	char dir1[] = "/daphna";
	sizeOfDir = getNumOfFileInDir(dir1);
	char buf1[sizeOfDir*20];
	ls (dir1,buf1);
	cout << buf1 << endl;

	moveFile(name3,name6);*/
	cout << "Bye Bye !!!!!!!!!!!!!!!!!!!!!!!" << endl;

}

void SystemCallLayer::test1() {
	char name1[] = "/daphna";
	int sizeOfDir = getNumOfFileInDir(name1);
	char buf1[sizeOfDir*20];
	ls(name1,buf1);
	cout << buf1 << endl;

	cout << "BYE BYE TEST1" << endl;
}



/***************************************** private methods*************************************************************/

vector<int> SystemCallLayer::findPathDirsInodeNums(vector<string> absolutePathVector, bool includeLastDir) {
	vector<int> dirsInodeNums;
	dirsInodeNums.clear();
	if (!includeLastDir) {
		absolutePathVector.erase(--absolutePathVector.end());
	}
	vector<string>::iterator pathIt;
	int currDirInodeNum = 0; //root
	dirsInodeNums.push_back(currDirInodeNum);
	for (pathIt = absolutePathVector.begin(); pathIt != absolutePathVector.end(); pathIt++) {
		vector<DirectoryEntry*> dirContent = fileSystem.d_read(currDirInodeNum);
		vector<DirectoryEntry*>::iterator directoryIt = dirContent.begin();
		string currDirInPath = *pathIt;
		bool found = false;
		while (!found && directoryIt != dirContent.end()) {
			if (currDirInPath.compare((*directoryIt)->getFileName()) == 0) {
				currDirInodeNum = (*directoryIt)->getINodeNum();
				dirsInodeNums.push_back(currDirInodeNum);
				found = true;
			}
			directoryIt++;

		}
		if (!found) {
			dirsInodeNums.clear();
			return dirsInodeNums;
		}
	}
	return dirsInodeNums;
}

void SystemCallLayer::addDirEntry(int containingDir, int dirInodeNum, char* name) {
	DirectoryEntry newDir(dirInodeNum,name);
	vector<DirectoryEntry*> newDirVec = fileSystem.d_read(containingDir);
	//TODO remove afterwards
	/*
	for (vector<DirectoryEntry*>::iterator it = newDirVec.begin(); it != newDirVec.end() ; it++) {
				int inodeNum = (*it)->getINodeNum();
				char* fileName = (*it)->getFileName();
				cout << inodeNum << endl;
				cout << fileName << endl;
			}
			*/
	newDirVec.push_back(&newDir);
	fileSystem.d_write(containingDir,newDirVec);
}

vector<string> SystemCallLayer::parsePath(string sStringToParse) {
	vector<string> parsedPath;
	parsedPath.clear();
	const char* str = sStringToParse.c_str();
	const char* seps = "/";
	char* token = NULL;
	int i = 0;

	/* Establish string and get the first token: */
	token =  strtok( (char*)str, seps );
	while( token != NULL ) {
		/* While there are tokens in "string" */
		parsedPath.push_back( string(token));
		i++;
		/* Get next token: */
		token = strtok( NULL, seps );
	}
	return parsedPath;
}

void SystemCallLayer::contextSwitch (int pid, map<int,FileDescEntry*>* newProcessTable) {
	currProcessID = pid;
	currProcessFileDescTable = newProcessTable;
	findNextLowestFreeDescriptorNum();
}

//operated when a child process inc. the open table entries inherited from his parent
void SystemCallLayer::incOpenTableConnectionsCounter (int fileInodeNum) {
	for (int i=0; i<(int)openFilesDescTable.size(); i++) {
		if (openFilesDescTable[i]->getPointerToInode() == fileInodeNum) {
			openFilesDescTable[i]->incNumOfConnectedProcesses();
			break;
		}
	}
}

bool SystemCallLayer::lockCheck (int fileInodeNum, string flag) {
	map<int,Lock>::iterator iter = inodeLocksTable.find(fileInodeNum);
	if (iter == inodeLocksTable.end()) {
		//the inode isn't locked
		return true;

	} else {
		string kind = iter->second.getKind();
		//if the file is read-only and the process wanted to open for read-only
		if (kind.compare("read-only") == 0 && flag.compare("read-only") == 0) {
			return true;
		}

		//if a process wants to open a file for read-and-write and it`s locked by HIM
		if (kind.compare("read-only") == 0 && flag.compare("read-and-write") == 0
				&& iter->second.getProcessID() == currProcessID) {
			return true;
		}

		if (kind.compare("read-and-write") == 0 && flag.compare("read-and-write") == 0
				&& iter->second.getProcessID() == currProcessID) {
				return true;
			}
		cerr << "Process " << currProcessID << " can't use the file for " << flag << ""
				", because it's locked by process " << iter->second.getProcessID() << ""
						". Lock is " << kind << endl;
		return false;
	}
}

bool SystemCallLayer::permissionsCheck (string permissions, string flag) {
	if (permissions.compare(flag) == 0) {
		return true;
	}

	if (permissions.compare("read-and-write") == 0 && flag.compare("read-only") == 0) {
		return true;
	}
	cerr << "No permission for " << flag << ". Permissions are " << permissions << endl;
	return false;
}

void SystemCallLayer::findNextLowestFreeDescriptorNum() {
	int fd=0;
	for (fd=0; fd<(int)currProcessFileDescTable->size(); fd++) {
		map<int,FileDescEntry*>::iterator iter = currProcessFileDescTable->find(fd);
		if (iter == currProcessFileDescTable->end()) {
			lowestFreeDescriptorNum = fd;
			return;
		}
	}
	//if the fd [0-(currProcessFileDescTable->size()-1)] are taken and there are free entries
	//in the process table, fd should be currProcessFileDescTable->size()

	if (currProcessFileDescTable->size()<12) {
		lowestFreeDescriptorNum = fd;
	} else {
		//there are no free entries left
		lowestFreeDescriptorNum = -1;
	}
}

void SystemCallLayer::fillSoftLink (int softLinkInode, unsigned char* target) {
	int size = strlen((char*)target);
	fileSystem.f_write(softLinkInode,target,0,size);
}

int SystemCallLayer::resetAndRemoveDir (const char* dirName) {
	//finds the real dir name (needed in case this is a soft-link):
	string stringPathName;
	findDir(dirName, stringPathName);
	if (stringPathName.compare("") == 0) {
		cout << "directory " << dirName << " doesn't exists or this is a broken soft-link" << endl;
		return -1;
	}

	ostringstream streamStringPathNameCopy;
	streamStringPathNameCopy << stringPathName << '\0';
	string stringPathNameCopy = streamStringPathNameCopy.str();

	vector<string> pathDirNames = parsePath(stringPathNameCopy);
	//extract the name of the desired directory for deletion
	string stringDirName = pathDirNames.at(pathDirNames.size()-1);
	vector<int> pathInodes = findPathDirsInodeNums(pathDirNames,true);
	if (stringPathName.compare("/") == 0) {
		cerr << "cannot delete \"/\"" << endl;
		return -1;
	}
	if (pathInodes.size() == 0) {
		cout << "directory " << stringPathName << " doesn't exists" << endl;
		return -1;
	}

	int parentDirInode = pathInodes.at(pathInodes.size()-2);
	vector<DirectoryEntry*> currentDirContent = fileSystem.d_read(parentDirInode);
	vector<DirectoryEntry*>::iterator it = currentDirContent.begin();
	int dirInodeNum = -1;
	bool found = false;
	while (!found && it != currentDirContent.end()) {
		DirectoryEntry* currDE = *it;
		if (strcmp((currDE->getFileName()),stringDirName.c_str()) == 0) {
			dirInodeNum = currDE->getINodeNum();
			found = true;
		} else {
			it++;
		}
	}
	vector<DirectoryEntry*> currentInerDirContent = fileSystem.d_read(dirInodeNum);
	for (int i=2; i<(int)currentInerDirContent.size(); i++) {
		cout << currentInerDirContent[i]->getFileName() << endl;
		char fileType = fileSystem.getFileType
				(currentInerDirContent[i]->getINodeNum());
		if (fileType == 'f') {
			if (fileIsOpen (currentInerDirContent[i]->getINodeNum())) {
				cerr << "Can't remove " << stringPathName << " it contains open files" << endl;
				return -1;
			} else {
				string fileToRemove = stringPathName + "/" + currentInerDirContent[i]->getFileName();
				RmFile(fileToRemove.c_str());
			}
		}

		if (fileType == 's') {
			string fileToRemove = stringPathName + "/" + currentInerDirContent[i]->getFileName();
			RmFile(fileToRemove.c_str());
		}

		if (fileType == 'd') {
			ostringstream streamDirToRemove;
			streamDirToRemove << stringPathName << "/" << currentInerDirContent[i]->getFileName() << '\0';
			string dirToRemove = streamDirToRemove.str();
			int retValue = resetAndRemoveDir(dirToRemove.c_str());
			if (retValue == -1) {
				return -1;
			}
		}
	}

	if (dirInodeNum != -1) {
		RmDir(stringPathName.c_str());
	} else {
		cout << "directory " << stringPathName << " doesn't exists" << endl;
		return -1;
	}
	return 0;
}

bool SystemCallLayer::fileIsOpen (int inodeNum) {
	for (int i=0; i<(int)openFilesDescTable.size(); i++) {
		if (openFilesDescTable[i]->getPointerToInode() == inodeNum) {
			return true;
		}
	}
	return false;
}
